{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Deep Learning"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "import tensorflow as tf\n",
    "import pandas as pd\n",
    "import numpy as np\n",
    "pd.set_option(\"display.max_rows\",15)\n",
    "%matplotlib inline\n",
    "\n",
    "sess = tf.InteractiveSession()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "tf.reset_default_graph()\n",
    "\n",
    "input_dim = 118\n",
    "classes = 2\n",
    "hidden_encoder_dim = 80\n",
    "\n",
    "latent_dim = 10\n",
    "\n",
    "hidden_decoder_dim = 80\n",
    "\n",
    "lam = 0.01\n",
    "\n",
    "def weight_variable(shape):\n",
    "    initial = tf.truncated_normal(shape, stddev=0.001)\n",
    "    return tf.Variable(initial)\n",
    "\n",
    "def bias_variable(shape):\n",
    "    initial = tf.constant(0.01, shape=shape)\n",
    "    return tf.Variable(initial)\n",
    "\n",
    "l2_loss = tf.constant(0.001)\n",
    "#learning_rate = tf.Variable(initial_value=0.001)\n",
    "\n",
    "with tf.variable_scope(\"Input\"):\n",
    "    x = tf.placeholder(\"float\", shape=[None, input_dim])\n",
    "    y_ = tf.placeholder(\"float\", shape=[None, classes])\n",
    "    \n",
    "    keep_prob = tf.placeholder(\"float\")\n",
    "\n",
    "with tf.variable_scope(\"Layer_Encoder\"):\n",
    "    W_encoder_input_hidden = weight_variable([input_dim,hidden_encoder_dim])\n",
    "    b_encoder_input_hidden = bias_variable([hidden_encoder_dim])\n",
    "    l2_loss += tf.nn.l2_loss(W_encoder_input_hidden)\n",
    "\n",
    "    # Hidden layer encoder\n",
    "    hidden_encoder = tf.nn.relu(tf.matmul(x, W_encoder_input_hidden) + b_encoder_input_hidden)\n",
    "    tf.summary.histogram(\"Weights_Encoder\", W_encoder_input_hidden)\n",
    "    hidden_encoder = tf.nn.dropout(hidden_encoder, keep_prob=keep_prob)\n",
    "\n",
    "with tf.variable_scope(\"Layer_Mean\"):\n",
    "    W_encoder_hidden_mu = weight_variable([hidden_encoder_dim,latent_dim])\n",
    "    b_encoder_hidden_mu = bias_variable([latent_dim])\n",
    "    l2_loss += tf.nn.l2_loss(W_encoder_hidden_mu)\n",
    "\n",
    "    # Mu encoder\n",
    "    mu_encoder = tf.matmul(hidden_encoder, W_encoder_hidden_mu) + b_encoder_hidden_mu\n",
    "    tf.summary.histogram(\"Weights_Mean\", W_encoder_hidden_mu)\n",
    "\n",
    "with tf.variable_scope(\"Layer_Variance\"):\n",
    "    W_encoder_hidden_logvar = weight_variable([hidden_encoder_dim,latent_dim])\n",
    "    b_encoder_hidden_logvar = bias_variable([latent_dim])\n",
    "    l2_loss += tf.nn.l2_loss(W_encoder_hidden_logvar)\n",
    "\n",
    "    # Sigma encoder\n",
    "    logvar_encoder = tf.matmul(hidden_encoder, W_encoder_hidden_logvar) + b_encoder_hidden_logvar\n",
    "    tf.summary.histogram(\"Weights_Variance\", W_encoder_hidden_logvar)\n",
    "\n",
    "with tf.variable_scope(\"Sampling_Distribution\"):\n",
    "    # Sample epsilon\n",
    "    epsilon = tf.random_normal(tf.shape(logvar_encoder), name='epsilon')\n",
    "\n",
    "    # Sample latent variable\n",
    "    std_encoder = tf.exp(0.5 * logvar_encoder)\n",
    "    z = mu_encoder + tf.multiply(std_encoder, epsilon)\n",
    "    tf.summary.histogram(\"Sample_Distribution\", z)\n",
    "    \n",
    "with tf.variable_scope(\"Layer_Decoder\"):\n",
    "    W_decoder_z_hidden = weight_variable([latent_dim,hidden_decoder_dim])\n",
    "    b_decoder_z_hidden = bias_variable([hidden_decoder_dim])\n",
    "    l2_loss += tf.nn.l2_loss(W_decoder_z_hidden)\n",
    "\n",
    "    # Hidden layer decoder\n",
    "    hidden_decoder = tf.nn.relu(tf.matmul(z, W_decoder_z_hidden) + b_decoder_z_hidden)\n",
    "    hidden_decoder = tf.nn.dropout(hidden_decoder, keep_prob=keep_prob)\n",
    "    tf.summary.histogram(\"Weights_Decoder\", W_decoder_z_hidden)\n",
    "\n",
    "with tf.variable_scope(\"Layer_Reconstruction\"):\n",
    "    W_decoder_hidden_reconstruction = weight_variable([hidden_decoder_dim, input_dim])\n",
    "    b_decoder_hidden_reconstruction = bias_variable([input_dim])\n",
    "    l2_loss += tf.nn.l2_loss(W_decoder_hidden_reconstruction)\n",
    "\n",
    "    x_hat = tf.matmul(hidden_decoder, W_decoder_hidden_reconstruction) + b_decoder_hidden_reconstruction\n",
    "    tf.summary.histogram(\"Weights_Reconstruction\", W_decoder_hidden_reconstruction)\n",
    "\n",
    "with tf.variable_scope(\"Layer_Dense_Hidden\"):\n",
    "    hidden_output = tf.layers.dense(z,latent_dim, activation=tf.nn.relu)\n",
    "\n",
    "with tf.variable_scope(\"Layer_Dense_Softmax\"):\n",
    "    y = tf.layers.dense(z, classes, activation=tf.nn.softmax)\n",
    "    \n",
    "with tf.variable_scope(\"Loss\"):\n",
    "    BCE = tf.reduce_mean(tf.nn.sigmoid_cross_entropy_with_logits(logits=x_hat, labels=x), reduction_indices=1)\n",
    "    KLD = -0.5 * tf.reduce_mean(1 + logvar_encoder - tf.pow(mu_encoder, 2) - tf.exp(logvar_encoder), reduction_indices=1)\n",
    "    softmax_loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(labels = y_, logits = y))\n",
    "    \n",
    "    loss = tf.reduce_mean(BCE + KLD + softmax_loss)\n",
    "\n",
    "    regularized_loss = tf.abs(loss + lam * l2_loss, name = \"Regularized_loss\")\n",
    "    correct_prediction = tf.equal(tf.argmax(y_, 1), tf.argmax(y, 1))\n",
    "    tf_accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32), name = \"Accuracy\")\n",
    "    \n",
    "    pred = tf.argmax(y, 1)\n",
    "    actual = tf.argmax(y_, 1)\n",
    "    \n",
    "    #tf.summary.scalar(\"BCE\", BCE)\n",
    "    #tf.summary.scalar(\"KLD\", KLD)\n",
    "    #tf.summary.scalar(\"Softmax_loss\", softmax_loss)\n",
    "    \n",
    "    tf.summary.scalar(\"loss\", regularized_loss)\n",
    "    \n",
    "with tf.variable_scope(\"Optimizer\"):\n",
    "    learning_rate=0.001\n",
    "    grad_clip=5\n",
    "    tvars = tf.trainable_variables()\n",
    "    grads, _ = tf.clip_by_global_norm(tf.gradients(regularized_loss, tvars), grad_clip)\n",
    "    train_op = tf.train.AdamOptimizer(learning_rate)\n",
    "    optimizer = train_op.apply_gradients(zip(grads, tvars))\n",
    "    \n",
    "# add op for merging summary\n",
    "summary_op = tf.summary.merge_all()\n",
    "\n",
    "# add Saver ops\n",
    "# saver = tf.train.Saver()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "kdd_train_2labels = pd.read_pickle(\"dataset/kdd_train_2labels_20percent.pkl\")\n",
    "kdd_test_2labels = pd.read_pickle(\"dataset/kdd_test_2labels_20percent.pkl\")\n",
    "\n",
    "\n",
    "output_columns_2labels = ['is_Attack','is_Normal']\n",
    "\n",
    "from sklearn import model_selection as ms\n",
    "from sklearn import preprocessing as pp\n",
    "\n",
    "x_input = kdd_train_2labels.drop(output_columns_2labels, axis = 1)\n",
    "y_output = kdd_train_2labels.loc[:,output_columns_2labels]\n",
    "\n",
    "ss = pp.StandardScaler()\n",
    "\n",
    "x_input = ss.fit_transform(x_input)\n",
    "\n",
    "x_test = kdd_test_2labels.drop(output_columns_2labels, axis = 1)\n",
    "y_test = kdd_test_2labels.loc[:,output_columns_2labels]\n",
    "\n",
    "x_test = ss.transform(x_test)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "scrolled": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Step 0 | Training Loss: 0.9374 | Validation Accuracy: 0.9101\n",
      "Step 2 | Training Loss: 0.2205 | Validation Accuracy: 0.9045\n",
      "Step 4 | Training Loss: 0.0413 | Validation Accuracy: 0.8873\n",
      "Step 6 | Training Loss: 0.1085 | Validation Accuracy: 0.8861\n",
      "Step 8 | Training Loss: 0.0310 | Validation Accuracy: 0.8986\n",
      "Step 10 | Training Loss: 0.0208 | Validation Accuracy: 0.8958\n",
      "Step 12 | Training Loss: 0.0736 | Validation Accuracy: 0.9014\n",
      "Step 14 | Training Loss: 0.0606 | Validation Accuracy: 0.8897\n",
      "Step 16 | Training Loss: 0.1363 | Validation Accuracy: 0.8819\n",
      "Step 18 | Training Loss: 0.1350 | Validation Accuracy: 0.8968\n",
      "Step 20 | Training Loss: 0.2812 | Validation Accuracy: 0.8928\n",
      "Step 22 | Training Loss: 0.0296 | Validation Accuracy: 0.8819\n",
      "Step 24 | Training Loss: 0.1350 | Validation Accuracy: 0.8803\n",
      "Step 26 | Training Loss: 0.0723 | Validation Accuracy: 0.8916\n",
      "Step 28 | Training Loss: 0.0329 | Validation Accuracy: 0.8883\n",
      "Step 30 | Training Loss: 0.1189 | Validation Accuracy: 0.8920\n",
      "Step 32 | Training Loss: 0.0622 | Validation Accuracy: 0.8968\n",
      "Step 34 | Training Loss: 0.0195 | Validation Accuracy: 0.9085\n",
      "Step 36 | Training Loss: 0.0328 | Validation Accuracy: 0.8958\n",
      "Step 38 | Training Loss: 0.0810 | Validation Accuracy: 0.9045\n",
      "Accuracy on Test data: 0.5327426195144653\n"
     ]
    }
   ],
   "source": [
    "epochs = 40\n",
    "batch_iterations = 100\n",
    "\n",
    "with tf.Session() as sess:\n",
    "    summary_writer_train = tf.summary.FileWriter('./logs/kdd/VAE/training', graph=sess.graph)\n",
    "    summary_writer_valid = tf.summary.FileWriter('./logs/kdd/VAE/validation')\n",
    "\n",
    "    sess.run(tf.global_variables_initializer())\n",
    "\n",
    "    for epoch in range(0, epochs):\n",
    "        x_train1, x_valid1, y_train1, y_valid1 = ms.train_test_split(x_input, \n",
    "                                      y_output.values, \n",
    "                                      test_size=0.2)\n",
    "        #x_valid, x_test, y_valid, y_test = ms.train_test_split(x_valid, y_valid, test_size = 0.4)\n",
    "        batch_indices = np.array_split(np.arange(x_train1.shape[0]), batch_iterations)\n",
    "\n",
    "        for i in batch_indices:\n",
    "            _, train_loss, summary_str = sess.run([optimizer, regularized_loss, summary_op],\n",
    "                                                  feed_dict={x: x_train1[i,:], y_: y_train1[i,:], keep_prob:0.6})\n",
    "            summary_writer_train.add_summary(summary_str, epoch)\n",
    "\n",
    "        \n",
    "        accuracy, summary_str = sess.run([tf_accuracy, summary_op], \n",
    "                                              feed_dict={x: x_valid1, y_:y_valid1, keep_prob:1})\n",
    "        summary_writer_valid.add_summary(summary_str, epoch)\n",
    "            \n",
    "        if epoch % 2 == 0:\n",
    "            print(\"Step {} | Training Loss: {:.4f} | Validation Accuracy: {:.4f}\".format(epoch, train_loss, accuracy))\n",
    "            \n",
    "    accuracy, pred_value, actual_value = sess.run([tf_accuracy, pred, actual], feed_dict={x: x_test, y_:y_test, keep_prob:1})\n",
    "    \n",
    "    print(\"Accuracy on Test data: {}\".format(accuracy))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "import itertools\n",
    "\n",
    "def plot_confusion_matrix(cm, classes,\n",
    "                          normalize=False,\n",
    "                          title='Confusion matrix',\n",
    "                          cmap=plt.cm.Blues):\n",
    "    \"\"\"\n",
    "    This function prints and plots the confusion matrix.\n",
    "    Normalization can be applied by setting `normalize=True`.\n",
    "    \"\"\"\n",
    "    np.set_printoptions(precision=4)\n",
    "\n",
    "    plt.imshow(cm, interpolation='nearest', cmap=cmap)\n",
    "    plt.title(title)\n",
    "    plt.colorbar()\n",
    "    tick_marks = np.arange(len(classes))\n",
    "    plt.xticks(tick_marks, classes, rotation=45)\n",
    "    plt.yticks(tick_marks, classes)\n",
    "\n",
    "    if normalize:\n",
    "        cm = cm.astype('float') / cm.sum(axis=1)[:, np.newaxis]\n",
    "        print(\"Normalized confusion matrix\")\n",
    "    else:\n",
    "        print('Confusion matrix, without normalization')\n",
    "\n",
    "    print(cm)\n",
    "\n",
    "    thresh = cm.max() / 2.\n",
    "    for i, j in itertools.product(range(cm.shape[0]), range(cm.shape[1])):\n",
    "        plt.text(j, i, cm[i, j].round(4),\n",
    "                 horizontalalignment=\"center\",\n",
    "                 color=\"white\" if cm[i, j] > thresh else \"black\")\n",
    "\n",
    "    plt.tight_layout()\n",
    "    plt.ylabel('True label')\n",
    "    plt.xlabel('Predicted label')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Normalized confusion matrix\n",
      "[[ 0.4613  0.5387]\n",
      " [ 0.1454  0.8546]]\n"
     ]
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAbUAAAGgCAYAAAAtsfn1AAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAIABJREFUeJzt3XmcXfP9x/HXeyb7KgsRSSRI0KCWxFJFKSVK0dbWKorS\nooultqpfdVHaKkUtpfYuRFWpPbRqF4l9C7GERCIb2dfJ5/fH+U7cTDKTSXJn7tx73k+P85hzvud7\nzvnemMd87ud7vud7FBGYmZlVgqpSN8DMzKxYHNTMzKxiOKiZmVnFcFAzM7OK4aBmZmYVw0HNzMwq\nhoOamZlVDAc1MzOrGA5qZmZWMVqVugFmZta0qrv0j1g8r2jni3lTHoiIYUU7YRE5qJmZVbhYPI+2\nmxxctPPNf+HynkU7WZE5qJmZVTyB8nG3KR+f0szMcsGZmplZpRMglboVzcJBzcwsD9z9aGZmVl6c\nqZmZ5YG7H83MrDJ49KOZmVnZcaZmZpYH7n40M7OKINz9aGZmVm6cqZmZVTzlpvvRmZqZWR6oqnhL\nYy4nvSfpZUkvSBqVyrpLGiHprfSzW0H9sySNlTRG0l4F5UPSecZKulRqODo7qJmZWVPZLSK2ioih\naftM4OGIGAQ8nLaRNBg4FNgMGAZcIak6HXMlcCwwKC0NvvLGQc3MLA+k4i2rb3/gxrR+I3BAQfkt\nEbEgIt4FxgLbSeoNdImIpyMigJsKjlkhBzUzs4qnZu9+BAJ4SNJoScelsl4RMTGtTwJ6pfU+wAcF\nx45PZX3Set3yenmgiJmZraqetffJkqsj4uo6dXaKiAmS1gFGSHqjcGdEhKQodsMc1MzMKl3xXz0z\nteA+2QpFxIT0c7KkO4DtgI8k9Y6IialrcXKqPgHoV3B431Q2Ia3XLa+Xux/NzPKgGbsfJXWU1Ll2\nHdgTeAW4CzgyVTsSuDOt3wUcKqmtpA3IBoSMTF2VMyXtkEY9HlFwzAo5UzMzs2LrBdyRRt+3Av4W\nEfdLehYYLukYYBxwMEBEvCppOPAasBg4MSJq0rlOAG4A2gP3paVeygaUmJlZparq3CfabvPdop1v\n/qM/G72y7sdScaZmZpYHVZ5RxMzMrKw4UzMzq3Sepd/MzKz8OFMzM8uDnMzS76BmZlbx5O5HMzOz\ncuNMzcwsD9z9aGZmFcPdj2ZmZuXFmZqZWaVb85d7lg0HNTOzPHD3o5mZWXlxULNck9Re0r8lzZB0\n2xqc5zBJDxazbaUiaWdJY0rdDiuy2i7IYiwtmIOalQVJ35Q0StJsSRMl3SdppyKc+kCydz/1iIiD\nVvckEfHXiNizCO1pUpJC0sCG6kTEYxGxSXO1yZqDmvUloaXUsltnBkg6BfgD8GuyALQ+cDmwXxFO\n3x94MyIWF+FcZU+S77NbWXNQsxZNUlfgF2Rvwv1nRMyJiEURcXdEnJ7qtJX0B0kfpuUPktqmfbtK\nGi/pVEmTU5Z3VNr3c+D/gENSBniMpHMl/aXg+gNSdtMqbX9b0juSZkl6V9JhBeWPFxy3o6RnU7fm\ns5J2LNj3iKRfSnoinedBST3r+fy17T+9oP0HSPqypDclTZf0k4L620l6StInqe4fJbVJ+x5N1V5M\nn/eQgvOfIWkScH1tWTpmo3SNbdL2epKmSNp1jf7HWvNz96NZi/A5oB1wRwN1zgZ2ALYCtgS2A35a\nsH9doCvQBzgGuFxSt4j4GVn2d2tEdIqIaxtqiKSOwKXA3hHRGdgReGEF9boD96S6PYCLgHsk9Sio\n9k3gKGAdoA3w4wYuvS7Zv0EfsiB8DfAtYAiwM3COpA1S3RrgZKAn2b/d7sAJABGxS6qzZfq8txac\nvztZ1npc4YUj4m3gDOAvkjoA1wM3RsQjDbTXWpraV8+4+9Gs5HoAU1fSPXgY8IuImBwRU4CfA4cX\n7F+U9i+KiHuB2cDq3jNaAmwuqX1ETIyIV1dQZx/grYi4OSIWR8TfgTeArxTUuT4i3oyIecBwsoBc\nn0XAeRGxCLiFLGBdEhGz0vVfIwvmRMToiHg6Xfc94E/AFxrxmX4WEQtSe5YREdcAY4FngN5kXyLM\nWiQHNWvppgE9V3KvZz1gXMH2uFS29Bx1guJcoNOqNiQi5gCHAN8DJkq6R9KmjWhPbZv6FGxPWoX2\nTIuImrReG3Q+Ktg/r/Z4SRtLulvSJEkzyTLRFXZtFpgSEfNXUucaYHPgsohYsJK61uJ4oIhZS/EU\nsAA4oIE6H5J1ndVaP5WtjjlAh4LtdQt3RsQDEfElsozlDbI/9itrT22bJqxmm1bFlWTtGhQRXYCf\nkHU+NSQa2impE9lAnWuBc1P3qpUb31MzK72ImEF2H+nyNECig6TWkvaW9NtU7e/ATyWtnQZc/B/w\nl/rOuRIvALtIWj8NUjmrdoekXpL2T/fWFpB1Yy5ZwTnuBTZOjyG0knQIMBi4ezXbtCo6AzOB2SmL\nPL7O/o+ADVfxnJcAoyLiO2T3Cq9a41aaNREHNWvxIuL3wClkgz+mAB8A3wf+lar8ChgFvAS8DDyX\nylbnWiOAW9O5RrNsIKpK7fgQmE52r6pu0CAipgH7AqeSdZ+eDuwbEVNXp02r6Mdkg1BmkWWRt9bZ\nfy5wYxodefDKTiZpf2AYn37OU4Btakd9WhnJSfejIhrseTAzszJXtVb/aLtr8cb3zL/zu6MjYmjR\nTlhELTvkmpmZrQLPHmBmVumkFt9tWCwOamZmedDCRy0WSz5Ct5mZ5YIzNTOzHFBOMjUHtdWktp2j\nquPKJmowW1bMn1PqJliZiYWziMXz1igiCQc1W4mqjj3puNfPS90MKzOL3hhZ6iZYmVkwZnipm1BW\nHNTMzCqdWPlkaRXCQc3MrOIpN92PHv1oZmYVw5mamVkO5CVTc1AzM8uBvAQ1dz+amVnFcKZmZpYD\necnUHNTMzCpdjob0u/vRzMwqhjM1M7MKpxw9p+agZmaWA3kJau5+NDOziuFMzcwsB/KSqTmomZnl\nQF6CmrsfzcysYjhTMzOrdDl6Ts1BzcwsB9z9aGZmVmacqZmZVTg/fG1mZhUlL0HN3Y9mZlYxnKmZ\nmeVBPhI1BzUzs4ondz+amZmVHWdqZmY5kJdMzUHNzCwH8hLU3P1oZmYVw5mamVmF88PXZmZWWfIR\n09z9aGZmlcOZmplZpcvRc2oOamZmOZCXoObuRzMzqxjO1MzMciAvmZqDmplZHuQjprn70czMKocz\nNTOzHHD3o5mZVQQpPzOKuPvRzMwqhoOamVkO1GZrxVgaeb1qSc9Lujttd5c0QtJb6We3grpnSRor\naYykvQrKh0h6Oe27VI24uIOamVkONHdQA34EvF6wfSbwcEQMAh5O20gaDBwKbAYMA66QVJ2OuRI4\nFhiUlmEru6iDmpmZFZWkvsA+wJ8LivcHbkzrNwIHFJTfEhELIuJdYCywnaTeQJeIeDoiArip4Jh6\neaCImVkeNO84kT8ApwOdC8p6RcTEtD4J6JXW+wBPF9Qbn8oWpfW65Q1ypmZmlgNF7n7sKWlUwXJc\nwXX2BSZHxOj62pIyr2iKz+lMzczMVtXUiBhaz77PA/tJ+jLQDugi6S/AR5J6R8TE1LU4OdWfAPQr\nOL5vKpuQ1uuWN8iZmplZpVPzDRSJiLMiom9EDCAbAPKfiPgWcBdwZKp2JHBnWr8LOFRSW0kbkA0I\nGZm6KmdK2iGNejyi4Jh6OVMzM6twAlrAs9cXAMMlHQOMAw4GiIhXJQ0HXgMWAydGRE065gTgBqA9\ncF9aGuSgZmZmTSIiHgEeSevTgN3rqXcecN4KykcBm6/KNR3UzMwqXn6myXJQMzPLgZzENA8UMTOz\nyuFMzcwsB/LS/ehMzczMKoYzNTOzSqf83FNzUDMzq3ACqqryEdXc/WhmZhXDmZqZWQ64+9HMzCqG\nRz+amZmVGWdqZmaVzqMfzcysUmSz9Ocjqjmo2TL22HI9fnPEtlRXiRv/O5aL73plhfW22bAHD/1i\nb4669FHuHPk+AF07tOay43ZkcN+1CIIT//QkI9+aygHb9+esA7dkk/W6sts59/L8O9MAGLJRDy75\nzueA7Fvk+f94kbtHfdA8H9SK6ks7foYLTzuQ6qoqbvjXk1x4/Yhl9u88ZBC3XXwc732Y/b+/8z8v\ncP7V99O2TSseuvYk2rRpRavqau546Hl+ddW9AHx24z5cdvahtG3bmsU1Szjp17cy6tVxHLr3UE46\nco+l595i0Hp87hu/4aU3V/r+SMsBBzVbqkri90dtz/6/HsGEaXN55Lwvc+/oDxgzYcZy9X7+zW34\nz0sfLlP+myO346EXJ3DEH/5H6+oqOrStBuC1Dz7hsIse4ZLv7LBM/dc++IQvnH0PNUuCXmu158kL\n9uW+58ZTs6RJ3vJuTaSqSvzhzIPZ5/g/MuGjT3j8r6dx9/9e5o13Ji1T74nn3+brP7pqmbIFCxcz\n7LhLmTNvIa1aVfGf607hwSdeY+TL73HeSQdw3tX38eATr7HXToM576QD2OvYS7jlvlHcct8oADYb\nuB7DLzrWAW2l8jNLvweK2FJDB/bgnUmzeG/ybBbVLOH2p95jn6H9lqv3vWGbctcz7zNl5vylZV3a\nt2bHTdfhpv+OBWBRzRJmzF0EwJsfzmDsxJnLnWfewpqlAaxd62ocysrTtpsP4O0PpvLehGksWlzD\nbQ88x767frbRx8+ZtxCA1q2qadWqmojsNyECunRsB0DXTu2ZOGXGcscePGwItz3wXBE+ReWTire0\nZM7UbKne3TowftqcpdsfTpvL0IE969Rpz77b9mOfXz7IFRvtuLS8/zqdmDZzAVd+b0c279+dF96Z\nxhk3PcvcBYsbvObQjXpy+fd2pF/Pjhx3+ePO0srQeut0ZfxHHy/dnvDRx2y3+YDl6u2w5QaMvPUs\nPpzyCWdddAevp0yuqko8+bcz2Kjf2vzp1kd59pVxAJx24T/49+Uncv7JX6WqSuz27d8vd84D99yG\ng06+umk+mJUlZ2q2Si44Ylt+9rfniDqxp1V1FVtu0J1rR7zJzmfdzdwFizllv5W/sHbU21PZ/rS7\n2PXsezl1/y1o29q/kpXohTc+YOO9z2G7Q87nylv+x/CLj1u6b8mSYIdDL2DgXj9l6Ob9GbxRbwCO\nO2hnTv/9Pxm09zmcfuHtXPmzw5Y557ab92fu/EW89vbEZv0s5UpS0ZaWrNn+gkh6cjWP20pSSBpW\nULaWpBMKtgdI+uYatO0RSUNX9/hKMfHjufTt0XHp9no9OvDhx3OXqbP1hj247oe78PKlX2P/7ftz\n0dHbs8/QfkyYNocJ0+cy6u2pAPzrmXFsuUH3Rl/7zQ9nMHvBIgb361acD2PN5sPJM+jb69P/b316\ndWNCna7CWXPmL+1mfODx12jdqpoea3Vcps6M2fP436g32XPHwQActu/2/OvhFwC4fcTzDN2s/zL1\nD9prCMPvH1X0z1ORitj12MJjWvMFtYjYceW1VugbwOPpZ621gBMKtgcAqx3ULDP67WlsuG5n+q/d\nidbVVXz9cwO4d/SyoxE/+6M72OKH/2SLH/6TO58ZxynXPcM9oz5g8oz5TJg2h4G9uwCw6+a9eWP8\n8vdACvVfuxPVaZLVfj07svF6XRk3ZXbTfDhrMqNeHcfA9dem/3o9aN2qmoP22oZ7HnlpmTq9enRe\nuj50s/5USUz7ZA49u3Wia6f2ALRr25rdt9+UMe99BMDEKTPYecggAHbdbmPGvj9l6Tkk8fU9t+G2\nB0Y39cezMtNs99QkzY6ITpJ6A7cCXdL1j4+Ix+o5RsBBwJeAxyS1i4j5wAXARpJeAEYAOwOfSds3\nAncANwO1XwW/HxFPpnOeAXwLWALcFxFnFlyvCrgOGB8RPy3uv0DLV7MkOO2Gkdxx1h5UV4mbHxnL\nG+NncPQeGwNw3UNvNnj8aTeM5M/f34k2rap576NZnPCnLDnfd2g/fvft7ejZpR23nf5FXn7vY756\nwUN8bpN1OHn/zVm0eAlLIjjlumeYPmtBk39OK66amiWc/Jvh/PuKE7NHQe58mtffmcR3DtwJgD//\n43G+usfWHHvQziyuqWH+/EUccdb1AKzbswvX/OJwqquqqKoSt494jvseyx4jOfGXf+N3px1Iq1ZV\nLFiwmO//6u9Lr7nTNgMZP+lj3pswrfk/cBnK03Nqiro3R5rqQp8GtVOBdhFxnqRqoENEzKrnmM8D\nv4iI3SX9Dbg9Im6XNAC4OyI2T/V2BX4cEfum7Q7AkoiYL2kQ8PeIGCppb+AcYI+ImCupe0RMl/QI\ncCbwI+CViDivnvYcBxwHoA49hnTa76Li/ONYbix6Y2Spm2BlZsGY4SyZO3mNIlLHPpvEZ46/auUV\nG2n0OV8cHREt8pZNKe7KPwscJelcYIv6AlryDeCWtH4Ly3ZBNqQ1cI2kl4HbgMGpfA/g+oiYCxAR\n0wuO+RMNBLRU/+qIGBoRQ9W2c33VzMysRJo9qEXEo8AuwATgBklHrKheyuK+DvyfpPeAy4BhkhoT\nTU4GPgK2BIYCbRpxzJPAbpLaNaKumVlZ8ejHJiKpP/BRRFwD/BnYpp6quwMvRUS/iBgQEf2B24Gv\nArOAwuBWd7srMDEilgCHA9WpfARZltghtaVweN61wL3AcEl+fs/MKopHPzadXYEXJT0PHAJcUk+9\nb5AN+Ch0O/CNiJgGPCHpFUm/A14CaiS9KOlk4ArgSEkvApsCcwAi4n7gLmBUGlTy48KTR8RFwPPA\nzWnQiJmZlZFmGyhSaaq7bxAd9/p5qZthZcYDRWxVFWWgSN9NYvMTizfzysif7NpiB4q4m83MrMJl\nQ/pL3Yrm0SKCmqRngLZ1ig+PiJdL0R4zMytPLSKoRcT2pW6DmVnlavmjFoulRQQ1MzNrWjmJaZ6l\n38zMKoczNTOzHHD3o5mZVYYyeGi6WNz9aGZmFcOZmplZhcvTq2cc1MzMciAvQc3dj2ZmVjGcqZmZ\n5UBOEjUHNTOzPHD3o5mZWZlxpmZmVuly9Jyag5qZWYVTjiY0dvejmZlVDGdqZmY5kJNEzUHNzCwP\nqnIS1dz9aGZmFcOZmplZDuQkUXNQMzOrdJIfvjYzMys7ztTMzHKgKh+JmoOamVkeuPvRzMyszDhT\nMzPLgZwkag5qZmaVTmTzP+aBux/NzKxiOFMzM8sBj340M7PKIL96xszMrOw4UzMzy4GcJGoOamZm\nlU741TNmZmZlx5mamVkO5CRRc1AzM8sDj340MzMrM87UzMwqXPaS0FK3onk4qJmZ5YBHP5qZmZWZ\nejM1SV0aOjAiZha/OWZm1hTykac13P34KhAs+29Rux3A+k3YLjMzK6Lcj36MiH4RsX762a/OtgOa\nmZmtkKR2kkZKelHSq5J+nsq7Sxoh6a30s1vBMWdJGitpjKS9CsqHSHo57btUK4nOjbqnJulQST9J\n630lDVm9j2pmZs0tmyareEsjLAC+GBFbAlsBwyTtAJwJPBwRg4CH0zaSBgOHApsBw4ArJFWnc10J\nHAsMSsuwhi680qAm6Y/AbsDhqWgucFWjPpaZmZVeevVMsZaViczstNk6LQHsD9yYym8EDkjr+wO3\nRMSCiHgXGAtsJ6k30CUino6IAG4qOGaFGpOp7RgR3wXmp8ZOB9o04jgzM6tMPSWNKliOq1tBUrWk\nF4DJwIiIeAboFRETU5VJQK+03gf4oODw8amsT1qvW16vxjyntkhSFVmURVIPYEkjjjMzsxaiyONE\npkbE0IYqREQNsJWktYA7JG1eZ39IiqK2isZlapcDtwNrp5t9jwO/KXZDzMys6TRn92OhiPgE+C/Z\nvbCPUpci6efkVG0C0K/gsL6pbEJar1ter5UGtYi4CfgpcCEwHTgoIm5pzIcxM7P8kbR2ytCQ1B74\nEvAGcBdwZKp2JHBnWr8LOFRSW0kbkA0IGZm6KmdK2iGNejyi4JgVauw0WdXAIrIuSM9CYmZWRmpH\nPzaj3sCNaQRjFTA8Iu6W9BQwXNIxwDjgYICIeFXScOA1YDFwYuq+BDgBuAFoD9yXlnqtNKhJOhv4\nJnAH2b/N3yT9NSLOX+WPaWZmJdGcD19HxEvA1isonwbsXs8x5wHnraB8FLD58kesWGMytSOArSNi\nLoCk84DnAQc1MzNrURoT1CbWqdcqlZmZWZnIxyRZDU9ofDHZPbTpwKuSHkjbewLPNk/zzMxsTUn5\nefVMQ5naK+nnq8A9BeVPN11zzMzMVl+9QS0irm3OhpiZWdPJSaLWqNGPG5GNSBkMtKstj4iNm7Bd\nZmZmq6wxz5zdAFxPdp9xb2A4cGsTtsnMzIqsVDOKNLfGBLUOEfEAQES8HRE/JQtuZmZWJqTiLS1Z\nY4b0L0gTGr8t6Xtk8251btpmmZmZrbrGBLWTgY7AD8nurXUFjm7KRpmZWfEIeUh/rfQOHIBZfPqi\nUDMzKxdl0G1YLA09fH0H6R1qKxIRX2uSFpmZma2mhjK1PzZbK8rQVhv04Imbjyh1M6zMjHh9r1I3\nwcrMKYc+VpTztPRRi8XS0MPXDzdnQ8zMrOnk5Z1hefmcZmaWA419SaiZmZUp4e7H5UhqGxELmrIx\nZmbWNJr5zdcls9LuR0nbSXoZeCttbynpsiZvmZmZ2SpqzD21S4F9gWkAEfEisFtTNsrMzIqrSsVb\nWrLGdD9WRcS4Ov2xNU3UHjMzK7JszsYWHo2KpDFB7QNJ2wEhqRr4AfBm0zbLzMxs1TUmqB1P1gW5\nPvAR8FAqMzOzMtHSuw2LpTFzP04GDm2GtpiZWRPJSe9jo958fQ0rmAMyIo5rkhaZmZmtpsZ0Pz5U\nsN4O+CrwQdM0x8zMik3gV8/UiohbC7cl3Qw83mQtMjOzosvLnIir8zk3AHoVuyFmZmZrqjH31D7m\n03tqVcB04MymbJSZmRVXTnofGw5qyp7W2xKYkIqWRES9Lw41M7OWR1Ju7qk12P2YAti9EVGTFgc0\nMzNrsRpzT+0FSVs3eUvMzKzJZFNlFWdpyertfpTUKiIWA1sDz0p6G5hDNjo0ImKbZmqjmZmtIc8o\nAiOBbYD9mqktZmZma6ShoCaAiHi7mdpiZmZNwA9fZ9aWdEp9OyPioiZoj5mZNYGcxLQGg1o10ImU\nsZmZmbV0DQW1iRHxi2ZriZmZNY0yeGN1saz0npqZmZU/5eRPekPPqe3ebK0wMzMrgnoztYiY3pwN\nMTOzppGNfix1K5pHY96nZmZmZS4vQS0vr9gxM7MccKZmZpYDysmDag5qZmYVLk/31Nz9aGZmFcOZ\nmplZpSuDV8YUi4OamVkO5GVCY3c/mplZxXCmZmZW4fI0UMRBzcwsB3LS++juRzMzqxzO1MzMKp6o\nysks/Q5qZmYVTrj70czMrOw4UzMzq3R+87WZmVUSP3xtZmZWZpypmZlVuDwNFHFQMzPLAXc/mpmZ\nlRlnamZmOZCTRM1Bzcys0on8dMvl5XOamVkOOFMzM6t0AuWk/9FBzcwsB/IR0tz9aGZmFcSZmplZ\nhcvefJ2PXM1BzcwsB/IR0tz9aGZmFcRBzcwsB6TiLSu/lvpJ+q+k1yS9KulHqby7pBGS3ko/uxUc\nc5aksZLGSNqroHyIpJfTvku1kmGcDmpmZhVPSMVbGmExcGpEDAZ2AE6UNBg4E3g4IgYBD6dt0r5D\ngc2AYcAVkqrTua4EjgUGpWVYQxd2UDMzs6KKiIkR8VxanwW8DvQB9gduTNVuBA5I6/sDt0TEgoh4\nFxgLbCepN9AlIp6OiABuKjhmhTxQxMyswpVymixJA4CtgWeAXhExMe2aBPRK632ApwsOG5/KFqX1\nuuX1clAzM8uBIs8o0lPSqILtqyPi6hVcsxNwO3BSRMwsbENEhKQoZqPAQc3MzFbd1IgY2lAFSa3J\nAtpfI+KfqfgjSb0jYmLqWpycyicA/QoO75vKJqT1uuX18j01M7McUBGXlV4rS8muBV6PiIsKdt0F\nHJnWjwTuLCg/VFJbSRuQDQgZmboqZ0raIZ3ziIJjVshBzZbx4AP389nNNmGzTQfyu99esNz+MW+8\nwRd2+hxdO7bl4osuXG5/TU0NOwzdmq/tv+/Ssl/94lw27N+H7YdsxfZDtuL+++5d5pj333+fnmt1\nWuH5rDys3akNu23cgy9u3IOBa3dYbn+rKrFt/7XYZWB3dh3Ug37d2i3dt/smPfnCoO7sMrA7O2/U\nfbljN+zZga9s0Ys21Z/+Oe3crhWf36gbuw7qwRcGdacqL08Wr640oXEzjn78PHA48EVJL6Tly8AF\nwJckvQXskbaJiFeB4cBrwP3AiRFRk851AvBnssEjbwP3NXRhdz/aUjU1NZz0wxO5574R9Onbl512\n2JZ9992PzwwevLROt+7d+f3Fl/Lvu/61wnP88dJL2OQzn2HWzJnLlP/gRydz8ik/XuExZ5x2CnsO\n27t4H8Sa3Rbrdebpdz9h3uIadt6oO5NmLmD2gpql+wf0aM/sBYt5dtxs2lSL3TbuyfhP5hPpjspT\n73zMwprlb6+0a13F2p3aMHfhp+cSsE3fLjw/fiYz5y+mdbVYUvQ7M7YmIuJx6k/qdq/nmPOA81ZQ\nPgrYvLHXdqZmSz07ciQbbTSQDTbckDZt2nDQIYdy97+XzfTXWWcdhm67La1bt17u+PHjx3P/ffdw\n1NHfafQ177rzXwwYsAGDB2+2xu230ujWoTVzFtYwd1ENEfDhjPms26XtcvVapXSqukosqlmyNKA1\nZLPenXl90uxlytbu3IaZ8xczc/5iABatIBjasmpHPxZraclaevusGX344QT69v30Xm2fPn2ZMKHB\ne7LLOO3Ukzjv/N9SVbX8r9WVl1/Gtlt/lu9+52g+/vhjAGbPns3vf/cbzj7nZ2veeCuZdq2qmLdo\nydLt+YuW0K519TJ13p02j05tW/GlTXuy66AevDJx1jL7d9igGzsP7M763dovLevVuS3zFy1ZGrxq\ndWyTdTBtPyDrztyo5/Ldnba8Zu5+LBkHNSuKe++5m3XWXodthgxZbt+x3z2e1998h2dGv8C6vXtz\n5mmnAtm9th/86GQ6derU3M21ZrZOpzbMnL+IEW9M5X9jp7PFel2WZm5PvD2dR8dO55l3P2ZAj/Z0\n79CaasGFuNpaAAAUTklEQVSgdToy5qPZy51Lgu4d2/D8BzN44u3prNu1LT07tmnuj2QtVJPdU5P0\nZETsuIrHvAeMjoivp+0DgX0j4tvFb2G9bTgXmB0RuRu1sN56fRg//oOl2xMmjKdPnwafc1zqqSef\n4O677+L+++9lwfz5zJw5k6OO+BbX3/QXevXqtbTe0cccy9cOyAaRPDvyGe745z84+6zTmfHJJ1RV\nVdGubTuOP/H7xf1g1qTmL15C+9affj9u17qK+YtqlqnTr1t7xk6ZA8DchTXMXVhDp7bVfDJvMfMX\nZ1newppg0swFrNWhNYtqgg5tqvnCoB5Lz7nLwB489vZ05i+qYdqchUvvwU2etZCu7Vsxdc7C5vi4\nZatl51fF02SZ2qoGtAJD0jxgq0ySB76sgaHbbsvYsW/x3rvvsnDhQm679Rb22Xe/Rh37y/PO5+33\nxjNm7Hvc9Ndb2HW3L3L9TX8BYOLEiUvr3fmvOxi8WXbP9+FHHmPM2PcYM/Y9vv/DkzjtzJ84oJWh\nT+YuomPbatq3rkKC9bq2Y9LMBcvUmbeohp6dsmyqTasqOratZu7CGqqV3WMDqFY2inLW/MXMWrCY\nB1+fwsNjpvLwmKnMX7SER8dOY8HiJUyZtZAu7VpRrewPdY+OrZm1YHHdZllONWWmNjsiOqUH7G4F\nuqTrHR8RjzVw6O+Bs4HD6pyvO3AdsCEwFzguIl5KmdVGqfx9SQ+QzQ3WkexZhwuBNmTDSxcAX46I\n6ZKOBY5L+8YCh0fE3JV8puPSMfRbf/3G/lOUjVatWnHxJX/kK/vsRU1NDUd++2gGb7YZ1/zpKgCO\n/e73mDRpEp/fYSizZs6kqqqKP176B55/6TW6dOlS73nPPvN0XnrxBSTRf8AALrviT831kawZBPDK\nh7PYYYNuCPjg4/nMXlBD/+7Z/bFx0+fx5uQ5bN23C18Y1B0Qr0+azcKaoEPraob27wpkL7Gc8Ml8\npsxuOONatCR4e+pcdh7YgwAmz1rA5FnO0lamhd8KKxpFY4Ygrc6JPw1qpwLtIuK8NOtyhzTB5YqO\neQ/YHngE+AqwFan7UdJlZE+x/1zSF4GLImKrFNS+AuwUEfMkfRv4KdlcY+3IAtYZEXGVpIuBcRHx\nB0k9ImJauu6vgI8i4rLGdj8OGTI0nnhmVENVzJYz4vWPSt0EKzOnHLonb7364hqFpEGbbRkX3fJg\nsZrEfp9dd/TKZhQpleYYKPIscFQKFlvUF9AK1AC/A86qU74TcDNARPwH6CGpNj24KyLmFdT9b0TM\niogpwAzg36n8ZWBAWt9c0mOSXibLCj2m3MyszDV5UIuIR4FdyObrukHSEY047OZ0TL+VVUzm1Nku\n7NBfUrC9hE+7XG8Avh8RWwA/J8vqzMwqUnO+JLSUmjyoSepP1rV3DdlUJ9us7JiIWARcDJxcUPwY\n6T6bpF3JuiJnLn90o3UGJqZJNw9bWWUzs/Klov7XkjXHaMFdgdMkLQJmk01I2RjXkt0bq3UucJ2k\nl8gGihy5ooNWwTlk7/eZkn52XsPzmZlZiTVZUIuITunnjXz6ptOVHTOgYH0BsF7B9nRW8MbTiDi3\nzvYNZF2LKzrn0n0RcSXZa8IbPJ+ZWSVo6d2GxeLnuszMKlw292M+olpJgpqkZ4C6M54eHhEvl6I9\nZmZWGUoS1CJi+1Jc18wsl8pg1GKxuPvRzCwH8hLUPEu/mZlVDGdqZmY50NKfLysWBzUzswonoCof\nMc3dj2ZmVjmcqZmZ5YC7H83MrGJ49KOZmVmZcaZmZpYD7n40M7OK4NGPZmZmZciZmplZxWv5L/cs\nFgc1M7NKl6MJjd39aGZmFcOZmplZDuQkUXNQMzOrdNnox3yENXc/mplZxXCmZmaWA/nI0xzUzMzy\nISdRzd2PZmZWMZypmZnlgB++NjOzipGTwY/ufjQzs8rhTM3MLAdykqg5qJmZ5UJOopq7H83MrGI4\nUzMzq3DCox/NzKxS+NUzZmZm5ceZmplZDuQkUXNQMzPLhZxENXc/mplZxXCmZmZW8eTRj2ZmVjk8\n+tHMzKzMOFMzM6twIjfjRBzUzMxyISdRzd2PZmZWMZypmZnlgEc/mplZxfDoRzMzszLjTM3MLAdy\nkqg5qJmZVbwcjel396OZmVUMZ2pmZjng0Y9mZlYRhEc/mpmZlR1namZmOZCTRM1BzcwsF3IS1dz9\naGZmFcOZmplZDnj0o5mZVQyPfjQzMyszztTMzHIgJ4mag5qZWS7kJKq5+9HMzIpK0nWSJkt6paCs\nu6QRkt5KP7sV7DtL0lhJYyTtVVA+RNLLad+l0srvDDqomZlVuGyS/uL91wg3AMPqlJ0JPBwRg4CH\n0zaSBgOHApulY66QVJ2OuRI4FhiUlrrnXI6DmplZpVM2+rFYy8pExKPA9DrF+wM3pvUbgQMKym+J\niAUR8S4wFthOUm+gS0Q8HREB3FRwTL0c1MzMrDn0ioiJaX0S0Cut9wE+KKg3PpX1Set1yxvkgSJm\nZjlQ5HEiPSWNKti+OiKubuzBERGSorhNyjiomZnlQXGj2tSIGLqKx3wkqXdETExdi5NT+QSgX0G9\nvqlsQlqvW94gB7XV9Nxzo6e2b61xpW5HC9QTmFrqRljZ8e9N/fqXugFFchdwJHBB+nlnQfnfJF0E\nrEc2IGRkRNRImilpB+AZ4AjgspVdxEFtNUXE2qVuQ0skadRqfIOznPPvTVNr9KjF4lxN+juwK1k3\n5XjgZ2TBbLikY4BxwMEAEfGqpOHAa8Bi4MSIqEmnOoFsJGV74L60NMhBzcwsB5pz7seI+EY9u3av\np/55wHkrKB8FbL4q1/boRzMzqxjO1KzYGj0CyqyAf2+akMjNLFkOalZcqzKs16yWf2+aQU6imrsf\nzcysYjhTMzPLgby8+dqZmpmZVQxnatZiSFKauNSsQZK6Az0j4s1St6VcNOeQ/lJypmYlJ6kfZPPB\nlbot1vJJagf8EDha0mdK3Z5yoSIuLZmDmjU7SZ0ktUnrnwF+K6lziZtlZSIi5gMPpc2D0vu4zAAH\nNWtmkjoCfwUOSkVz0zJbUutUp6V/GbQSqf3diIjHyeYM7AIc6MC2Es38PrVSclCzZhURc4BbgaMk\nHQIMAOZFZlGq425IW07tPVdJG0hqFRFPAtcDXckCm7siG5SPDkgPFLFmI6k6Imoi4m+SpgBnAKOB\nDSRdQvYSwAVAq4i4qJRttZYnBbR9gHOAxyTNBv5ANhvJMcC3JP01Il4rZTuttJypWbNI37JrJH1J\n0m8jYgRwCdkEpwuB99PPTmSvmTBbRnoFya+BQ8i+kB8A/BaYAtwIdCT7HbI6RH66H52pWbNI37J3\nB64AvpvK/i1pMXAK8GZE/LuUbbSWSVIVEGTvXDsC2BTYBTgTOA64kCzrPzt1b9sKtPBYVDTO1KzJ\nKdMKGAacExH/qR39GBH3AVcBZ0jqU8p2WstSMGCoU7rnendEvEiWoX0nIh4ge3tyK6CXA5qBg5o1\ng/QHaTEwH9hBUruIWAggaVvgXmC/iFjpq9otPwruoT0s6VxJX0u71gGOk7Q9sB1wYUS8UrKGlom8\ndD86qFmTqP2WLWl9SX1T8X1Aa+ALad+WwMXAxhExvSQNtRZLUm/gMLLuxenAXinIHQ30A/4POD8i\nXipdK8uHivhfS+Z7atYkCr5lnw88Kal7RBychl0fLukMsqHYv0pdSmZLSRoKbAlMiIhbJa0N7AV8\nFWgdEftK6hARcz29mhVyULOiKniWaAeykWn7kmVm10l6KCL2kHQD2R+sGRHxtv8oWSFJu5KNZnyA\nbJj+3yPiOUn3AW2A/SWNjIgPwc81NlrLTrCKxkHNiiLNx7coDdvvBUwDDgYGkY127Ao8IunJiNgR\neK72WP9RslqSNgB+AhweEY9KGgv8RdJhEfG8pDuB+2sDmjVeTmKa76nZmktDrncETpK0L9m9jlnA\na8A+wHURMYvs2/f6aXCIGbDM/ddtybL6rmQjHImI3wLXAndJGhIR0xzQrCEOalYsLwF7AjcD/4iI\nSWRfDicCG0k6lqwr8ksR8WzpmmktTequ3oWsu/plsgesO0j6ftr/e+BysgfzbTUUc+SjRz9axZLU\nUVLfiFgC9E/F/wX2TsP2l5DNpj6XLKBdFRGvl6i51kJJ2gQ4HrghIkYDjwAPA5tKOhUgIi6IiP95\nsuvVl5fRjw5qtiYGAJdJOhv4MXAq8AOymdNr5258hyzQfT0i/uk/SrYCWwC9gD0krR0RM4D7gSeB\nTSTVfmHy/VdbKQc1W20R8SowluzG/jPpAdgpZFNhtZX0MNm37kXp4Wv/UbLCe2h9JXWNiH+QTVI8\nk2y2/R7pHuy/gf+LiHElbG7lyMck/R79aKtG0lrAwoiYm4peAX4PHCHp5Yh4GHgpZW9fAj6MiKdL\n1FxrYSRVRcQSSXuT3UMbI2kdsoEhdwN7kz3HeHNETCMbcGRF0MJjUdE4qFmjSeoOvAk8JOmxiLg8\nIm5M+z4ALpJ0JPAJ8LXa18f4OTST1D4i5qWANhD4JfDdiHhS0qXAv8germ6dfnYkeyzEbJU4qNmq\n+Bh4kGxE42GStgMeB26LiGskLQRuBxYDJ9Ue5ICWb5K6AhdIuiMiHiT70vMG2RckIuKHkv4OnBkR\nP5P0bERMLGGTK1Je7mb7npo1WgpOz5Hd1N8FuCH9/J+k3cgGhGxPNijkvlK101qcLmT3Xr+ZXj80\nE+gB7FFQ517Su9Ac0JpCMcc+tuzo6EzNVklEXCjpXrI/SK8AW5F96z4UGAgc4hnTDUBS54iYFREf\nSLqJ7HfkaLLBRD8BbpC0KTAjlZ9eutZapXBQs0aTVB0RNWQZ2lfJZti/NgW6dcgmmp1ayjZayyBp\nAPAPSaOB4cBbwPXAArJHP34DHEQ2MGQ94OSIeMj3X5tG7Zuv88BBzRotBTSAZ4Bzgaci4sJUNsV/\njKxAO6A3sD/wHtmMIFcB3ciePzsHOC8iLik8yL9DtqZ8T81WSfomPQ44BehU+7Zq/zGyWmnY/htk\nXdQzgPeBQ4APyeZ2PDBt/1bSWmnuULOicKZmyyl4fUxVmupqqYLgNR5YsvzRlndp2H5VRLwu6VvA\nLcCvI+JaSf8ge3PD/sALEfFJSRubI+5+tFwqCGi7k2ViD0TE/Lr1IuIVSWdExIQSNNNauILA9qyk\nQ4G/p/lALwfGkD147WcYm1FLH7VYLE77bak0ECQkDQOuBD5eUUBTpioixknqIKlH87fWWrrCwEbW\n3XiOpBPr1HFAs6JyUDMkDUzDr2skdSO7if+99JLGnSUdmR60rlU71dFaZM+mdS9Jw61FKJjLcbm/\nJwWBbTTwFeDV5m6fATl69Yy7Hw2yh6nXkfR0RHws6b/AMekdaFXAIrL7ICMltYqIxWmWiNuA0yLi\nrdI13UqpMd3VdTI2dzmWQBnMQ1w0ztSMiHiC7OWM70jqQvYc2kjgsog4hOw5o80ktUkBrRtwB/CL\niHi0VO220mpsd3Vt9XRMe7Jh/WZNwkHNAEiv+vgR2TNEUyPikjTZ7M5kk8/+OSIWpurfAH4VEY+V\nqLlWQqvaXV370H7qrn6EbIosa25+9YzlTUTcKWkRMFrSEGA+2TNFP42Ie2q7jSLiitK21ErM3dVl\nKC+jHx3UbBkRca+kJcDrwCbAGRExv+Deie+H5FxEPCGpM1l39WfJuqv3AZ5N2f1+wFGpu3phyuZu\nB37m7N6amrsfbTkRcT/wHWDr2nsktYHMAc3A3dXlyKMfLdci4h7wSDWrn7ury0sLj0VF46BmDXJA\ns4a4u9paGnc/mtkacXd1mfDoRzOzxnF3dcuXl9GPztTMrGgc0KzUnKmZmVW4PL35Wv5iZWZW2STd\nD/Qs4imnRsSwIp6vaBzUzMysYviemlUsSTWSXpD0iqTbJHVYg3PtKunutL6fpDMbqLuWpBNW4xrn\nSvpxY8vr1LlB0oGrcK0Bkl5Z1TaatXQOalbJ5kXEVhGxObAQ+F7hztqXna7qSSPiroi4oIEqawGr\nHNTMbM05qFlePAYMTBnKGEk3Aa8A/STtKekpSc+ljK4TgKRhkt6Q9BzwtdoTSfq2pD+m9V6S7pD0\nYlp2BC4ANkpZ4u9SvdMkPSvpJUk/LzjX2ZLelPQ42cPLDZJ0bDrPi5Jur5N97iFpVDrfvql+taTf\nFVz7u2v6D2nWkjmoWcWT1ArYm+ydcZDNIH9FRGwGzAF+CuwREdsAo4BTJLUDriF7W/MQYN16Tn8p\n8L+I2BLYhuzNzmcCb6cs8TRJe6ZrbgdsBQyRtEuaWurQVPZlYNtGfJx/RsS26XqvA8cU7BuQrrEP\ncFX6DMcAMyJi23T+YyVt0IjrmJUlD+m3StZe0gtp/THgWmA9YFxEPJ3KdwAGA08oG/PcBngK2BR4\nt/Y1KZL+Ahy3gmt8ETgCICJqgBlpVvpCe6bl+bTdiSzIdQbuiIi56Rp3NeIzbS7pV2RdnJ2ABwr2\nDY+IJcBbkt5Jn2FP4LMF99u6pmu/2YhrmZUdBzWrZPMiYqvCghS45hQWASMi4ht16i1z3BoScH5E\n/KnONU5ajXPdABwQES9K+jawa8G+ukOZI137BxFRGPyQNGA1rm3W4rn70fLuaeDzkgYCSOooaWPg\nDWCApI1SvW/Uc/zDwPHp2Or0MsxZZFlYrQeAowvu1fWRtA7wKHCApPbp/WRfaUR7OwMTJbUGDquz\n7yBJVanNGwJj0rWPT/WRtLGkjo24jllZcqZmuRYRU1LG83dJbVPxTyPiTUnHAfdImkvWfdl5Baf4\nEXC1pGOAGuD4iHhK0hNpyPx96b7aZ4CnUqY4G/hWRDwn6VbgRWAy8GwjmnwO8AwwJf0sbNP7wEig\nC/C9NFv+n8nutT2n7OJTgAMa969jVn788LWZmVUMdz+amVnFcFAzM7OK4aBmZmYVw0HNzMwqhoOa\nmZlVDAc1MzOrGA5qZmZWMRzUzMysYvw/G8VDWD86R00AAAAASUVORK5CYII=\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x7fc820599710>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "from sklearn.metrics import confusion_matrix\n",
    "cm_2labels = confusion_matrix(y_pred = pred_value, y_true = actual_value)\n",
    "plt.figure(figsize=[6,6])\n",
    "plot_confusion_matrix(cm_2labels, output_columns_2labels, normalize = True)"
   ]
  }
 ],
 "metadata": {
  "_draft": {
   "nbviewer_url": "https://gist.github.com/441dbeee083527e615728d6d3057ba04"
  },
  "gist": {
   "data": {
    "description": "With Truth Table: Getting 90% accuracy with KDDTest+",
    "public": false
   },
   "id": "441dbeee083527e615728d6d3057ba04"
  },
  "kernelspec": {
   "display_name": "Python [conda env:p3]",
   "language": "python",
   "name": "conda-env-p3-py"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.0"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
